/**
 * 
 */
package com.oauth.facebook.server.service;

import java.util.Collections;
import java.util.Comparator;
import java.util.EnumSet;
import java.util.List;
import java.util.Set;

import javax.annotation.PostConstruct;
import javax.servlet.http.HttpServletRequest;

import org.gwtwidgets.server.spring.ServletUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.google.code.facebook.graph.client.oauth.FacebookAccessToken;
import com.google.code.facebook.graph.client.oauth.FacebookOAuthService;
import com.google.code.facebook.graph.client.oauth.FacebookOAuthServiceFactory;
import com.google.code.facebook.graph.model.enumeration.Permission;
import com.oauth.common.server.util.OAuthStringUtil;
import com.oauth.facebook.client.beans.FacebookUser;
import com.oauth.facebook.server.beans.FbUser;
import com.oauth.facebook.server.beans.Result;
import com.oauth.facebook.server.conf.FacebookInfoBean;
import com.oauth.facebook.server.conf.FacebookOAuthConfig;
import com.oauth.facebook.server.util.FacebookUtil;
import com.oauth.logging.OAuthLogger;
import com.oauth.service.application.ApplicationService;
import com.oauth.service.generic.beans.OAuthServiceBean;
import com.oauth.service.global.beans.Birthday;
import com.oauth.service.global.beans.OAuthAccessToken;
import com.oauth.service.global.beans.OAuthRequestToken;
import com.rapwaves.feeds.Feeds;
import com.restfb.Connection;
import com.restfb.DefaultFacebookClient;
import com.restfb.Parameter;
import com.restfb.types.Post;
import com.restfb.types.User;

/**
 * @author asanthan
 * 
 */
@Service
public class FacebookServiceImpl implements FacebookService {

	private static final Parameter[] EMPTY_PARAMETER = new Parameter[0];

	private final OAuthLogger logger = OAuthLogger.getLogger(this.getClass());

	private FacebookOAuthService facebookOauthService;

	@Autowired
	private ApplicationService applicationService;

	@Autowired
	private FacebookOAuthConfig facebookOAuthConfig;

	@Autowired
	private FacebookInfoBean facebookInfoBean;

	private Set<Permission> permissionSet;

	@PostConstruct
	public void initialize() {
		facebookOauthService = FacebookOAuthServiceFactory.getInstance()
				.createFacebookOAuthService(facebookOAuthConfig.getConsumerKey(),
						facebookOAuthConfig.getConsumerSecret());

		permissionSet = EnumSet.of(Permission.EMAIL, Permission.FRIENDS_ABOUT_ME,
				Permission.FRIENDS_ACTIVITIES, Permission.FRIENDS_BIRTHDAY,
				Permission.FRIENDS_EVENTS, Permission.FRIENDS_LIKES, Permission.FRIENDS_STATUS,
				Permission.FRIENDS_VIDEOS, Permission.FRIENDS_PHOTOS, Permission.PUBLISH_STREAM,
				Permission.READ_FRIENDLISTS, Permission.READ_MAILBOX, Permission.READ_REQUESTS,
				Permission.USER_ACTIVITIES, Permission.USER_BIRTHDAY, Permission.USER_EVENTS,
				Permission.USER_LIKES, Permission.USER_STATUS, Permission.USER_PHOTOS,
				Permission.USER_VIDEOS);
	}

	private DefaultFacebookClient getFacebookClient(Long pMemberId) throws Exception {
		OAuthAccessToken accessToken = getAccessTokenFromDB(pMemberId);
		DefaultFacebookClient client = new DefaultFacebookClient(accessToken.getToken());
		return client;
	}

	private OAuthAccessToken getAccessTokenFromDB(Long pMemberId) throws Exception {
		OAuthServiceBean token = null;
		token = applicationService.getToken(pMemberId, facebookInfoBean.getKey());
		if (token != null) {
			return new OAuthAccessToken(token.getAccessToken(), token.getAccessTokenSecret());
		}
		return null;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @seecom.oauth.facebook.server.service.FacebookService#
	 * getAccessTokenFromRequestToken
	 * (com.oauth.common.server.beans.OAuthRequestToken, java.lang.String)
	 * 
	 * NOTE: We are not using request token from the parameter.
	 */
	@Override
	public OAuthAccessToken getAccessToken(OAuthRequestToken requestToken) throws Exception {
		HttpServletRequest request = ServletUtils.getRequest();
		String verifier = request.getParameter("code");
		if (OAuthStringUtil.isBlank(verifier)) {
			throw new Exception("Verifier not found");
		}
		FacebookAccessToken accessToken = facebookOauthService.getAccessToken(
				facebookOAuthConfig.getCallbackUrl(), verifier);
		// Get access token .
		return new OAuthAccessToken(accessToken.getAccessToken(), accessToken.getAccessToken());
	}

	@Override
	public OAuthRequestToken getRequestToken() throws Exception {
		String authzUrl = facebookOauthService.getAuthorizationUrl(
				facebookOAuthConfig.getCallbackUrl(), permissionSet);
		return new OAuthRequestToken(null, null, authzUrl);
	}

	@Override
	public List<FacebookUser> getFriends(Long pMemberId) throws Exception {
		DefaultFacebookClient facebookClient = getFacebookClient(pMemberId);
		Connection<User> connections = facebookClient.fetchConnection("me/friends", User.class,
				Parameter.with("fields", "id, name,picture"));
		return FacebookUtil.getUsers(connections);
	}

	@Override
	public List<Birthday> getUpcomingBirthdays(Long pMemberId, int pBirthdayInterval)
			throws Exception {
		DefaultFacebookClient facebookClient = getFacebookClient(pMemberId);
		List<FbUser> userList = getUserWithUpcomingBirthday(facebookClient, pBirthdayInterval);
		return FacebookUtil.getBirthdays(userList);
	}

	@Override
	public List<FacebookUser> getMessages(Long pMemberId) throws Exception {
		// getLinkedInClient(pMemberId).getp
		return null;
	}

	@Override
	public List<FacebookUser> getUpdates(Long pMemberId) throws Exception {
		DefaultFacebookClient facebookClient = getFacebookClient(pMemberId);
		Connection<Post> myFeed = facebookClient.fetchConnection("me/home", Post.class);
		return FacebookUtil.getUpdates(myFeed);

	}

	private List<FbUser> getUserWithUpcomingBirthday(DefaultFacebookClient pFacebookClient,
			int pBirthdayInterval) throws Exception {
		String fqlQuery = FacebookUtil.getBirthdayFQLQuery(pBirthdayInterval);
		List<FbUser> userList = pFacebookClient.executeQuery(fqlQuery, FbUser.class,
				EMPTY_PARAMETER);
		// SORT userlist.
		Collections.sort(userList, new BirthdayComparator());
		return userList;
	}

	private String getStatus(Long pFacebookUserId, DefaultFacebookClient pFacebookClient)
			throws Exception {
		List<Result> results = pFacebookClient.executeQuery("select message from status where uid="
				+ pFacebookUserId, Result.class);
		if (results != null && !results.isEmpty()) {
			return results.get(0).getMessage();
		}
		return "What's on your mind?";
	}

	@Override
	public FacebookUser getFacebookUser(Long pMemberId) throws Exception {
		DefaultFacebookClient facebookClient = getFacebookClient(pMemberId);
		User user = facebookClient.fetchObject("me", User.class,
				Parameter.with("fields", "id,name,picture"));
		FacebookUser user2 = FacebookUtil.getUser(user);
		user2.setStatus(getStatus(user2.getId(), facebookClient));
		return user2;

	}

	@Override
	public void updateStatus(String pText, Long pMemberId) throws Exception {
		System.out.println("Updating..." + pText);
		DefaultFacebookClient facebookClient = getFacebookClient(pMemberId);
		facebookClient.publish("me/feed", Post.class, Parameter.with("message", pText));
	}

	static class BirthdayComparator implements Comparator<FbUser> {
		@Override
		public int compare(FbUser pUser1, FbUser pUser2) {
			return pUser1.getBirthdayDate().compareTo(pUser2.getBirthdayDate());
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * com.oauth.facebook.server.service.FacebookService#getFeeds(java.lang.
	 * Long)
	 */
	@Override
	public Feeds getFeeds(Long pMemberId) throws Exception {
		DefaultFacebookClient facebookClient = getFacebookClient(pMemberId);
		Connection<Post> myFeed = facebookClient.fetchConnection("me/home", Post.class);
		return FacebookUtil.getFeedUpdates(myFeed);
	}

}
